using System;
using System.Linq;
using System.Collections.Generic;
using GodPl.WorldState;
using GodPl.GameObjects;
using GodPl.GameObjects.Creatures;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace GodPl
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class GodPlGame : Microsoft.Xna.Framework.Game
    {
        #region Global Constants
        public static readonly int ScreenWidth = 1024;
        public static readonly int ScreenHeight = 768;

        public static Random RandomGen = new Random();

        #endregion

        static GameScreen gameScreen;
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        IntPtr drawSurface;
        static World world;

        /// <summary>
        /// The last recorded game time.
        /// </summary>
        /// <remarks>Note that this is static. Assuming only one GodPlGame instance at a time.</remarks>
        static GameTime lastGameTime;

        public static World World
        {
            get { return world; }
        }

        public GameTime GameTime
        {
            get { return lastGameTime;}
        }

        public bool Paused;
        

        /// <summary>
        /// </summary>
        /// <remarks>
        /// Code for enabling drawing to Picture Box by Roy A. Triesscheijn
        /// (http://royalexander.wordpress.com/2008/10/09/xna-30-and-winforms-the-easy-way/)
        /// </remarks>
        /// <param name="drawSurface"></param>
        public GodPlGame(GameScreen gameScreen, IntPtr drawSurface)
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            GodPlGame.gameScreen = gameScreen;
            this.drawSurface = drawSurface;

            graphics.PreferredBackBufferWidth = ScreenWidth;
            graphics.PreferredBackBufferHeight = ScreenHeight;

            
            graphics.PreparingDeviceSettings +=
              new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
            
            System.Windows.Forms.Control.FromHandle((this.Window.Handle)).VisibleChanged +=
              new EventHandler(Game1_VisibleChanged);

        }

        /// <summary>
        /// Event capturing the construction of a draw surface and makes sure this gets redirected to
        /// a predesignated drawsurface marked by pointer drawSurface
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle =
            drawSurface;
            //e.GraphicsDeviceInformation.PresentationParameters.BackBufferHeight = ScreenWidth;
            //e.GraphicsDeviceInformation.PresentationParameters.BackBufferWidth = ScreenHeight;
        }

        /// <summary>
        /// Occurs when the original gamewindows' visibility changes and makes sure it stays invisible
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Game1_VisibleChanged(object sender, EventArgs e)
        {
            if (System.Windows.Forms.Control.FromHandle((this.Window.Handle)).Visible == true)
            {
                System.Windows.Forms.Control.FromHandle((this.Window.Handle)).Visible = false;
            }
        }
 



        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            world = new World(this);

            gameScreen.nudTemperature.Value = (decimal)world.Weather.Temperature;

            base.Initialize();
        }

        public static void StartPlaying()
        {
            world.Populate(World.WorldLayout.Default);
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            lastGameTime = gameTime;

            world.Weather.Temperature = (float)gameScreen.nudTemperature.Value;
            world.Update(gameTime);

            if (Every(0, 0, 1))
            {
                UpdateWidgets();
            }
            

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();

            world.Draw(gameTime, spriteBatch);

            spriteBatch.End();
            base.Draw(gameTime);
        }


        public void UpdateWidgets()
        {
            //var ids = from s in World.GameObjects
            //          where s.GetType() == typeof(Briyya)
            //          select ((Briyya)s).Id.ToString();

            //gameScreen.listboxCreatureList.Items.Clear();
            //gameScreen.listboxCreatureList.Items.AddRange(ids.ToArray());

            try
            {

                int numCreatures = world.Briyyas.Count;

                gameScreen.nudSelector.Maximum = numCreatures;
                int selected = (int)gameScreen.nudSelector.Value;
                gameScreen.txtCreatureInfo.Text = world.Briyyas[selected].ToString();

                for (int i = 0; i < numCreatures; ++i)
                {
                    world.Briyyas[i].Selected = false;
                }

                world.Briyyas[selected].Selected = true;
            }
            catch (Exception e)
            { }

        }


        /// <summary>
        /// Given a time interval, returns whether the game time is at a multiple of that interval.
        /// </summary>
        /// <param name="h">Hours</param>
        /// <param name="min">Minutes</param>
        /// <param name="sec">Seconds</param>
        /// <returns></returns>
        public static bool Every(int h, int min, int sec)
        {
            if (lastGameTime.TotalGameTime.Milliseconds != 0)
            {
                return false;
            }

            int intervalSeconds = 3600 * h + 60 * min + sec;

            if (((int)lastGameTime.TotalGameTime.TotalSeconds % intervalSeconds) == 0)
            {
                return true;
            }

            return false;
        }

        public static bool Every(TimeSpan duration)
        {
            return Every(duration.Hours, duration.Minutes, duration.Seconds);
        }

        public static TimeSpan ParseTimeLiteral(string lit)
        {
            lit = lit.Trim();
            
            if (lit.Length != 8)
            {
                throw new ArgumentException("Bad time literal");
            }

            int h = int.Parse(lit.Substring(0, 2));
            int m = int.Parse(lit.Substring(3, 2));
            int s = int.Parse(lit.Substring(6, 2));

            return new TimeSpan(h, m, s);
        }

        /// <summary>
        /// Write a line to the program output. Currently goes to console, will be replaced by on-screen
        /// text-box.
        /// </summary>
        /// <param name="str"></param>
        public static void LogLine(string str)
        {
            Console.WriteLine(str);
            gameScreen.txtLog.AppendText("\r\n\r\n" + str);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <remarks>TODO: Handle empty cases</remarks>
        /// <param name="t"></param>
        /// <returns></returns>
        public static Object PickRandom(Type t)
        {
            var choices = GetAllOf(t);
            if (choices.Count() == 0)
            {
                return null;
            }
            return (choices.ElementAt(GodPlGame.RandomGen.Next(0, choices.Count())));
        }

        public static IEnumerable<Object> GetAllOf(Type t)
        {
            var choices = from c in World.GameObjects
                          where c.GetType() == t
                          select (Object)c;

            return (IEnumerable<Object>)choices;
        }

        public static bool Roll(float prob)
        {
            float outcome = (float)RandomGen.NextDouble();
            return (outcome <= prob);
        }

    }
}
